Skip to contentMethod: static {...}
1: /*
2: * *********************************************************************************************************************
3: *
4: * TheseFoolishThings: Miscellaneous utilities
5: * http://tidalwave.it/projects/thesefoolishthings
6: *
7: * Copyright (C) 2009 - 2023 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *********************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
12: * the License. You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
17: * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
18: * specific language governing permissions and limitations under the License.
19: *
20: * *********************************************************************************************************************
21: *
22: * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
23: * git clone https://github.com/tidalwave-it/thesefoolishthings-src
24: *
25: * *********************************************************************************************************************
26: */
27: package it.tidalwave.role;
28:
29: import javax.annotation.Nonnull;
30: import java.util.Collections;
31: import java.util.Iterator;
32: import java.util.List;
33: import java.util.Objects;
34: import java.util.ServiceLoader;
35: import java.util.function.Supplier;
36: import it.tidalwave.util.NotFoundException;
37: import it.tidalwave.util.Task;
38: import it.tidalwave.util.LazySupplier;
39: import it.tidalwave.role.spi.ContextManagerProvider;
40: import lombok.AccessLevel;
41: import lombok.NoArgsConstructor;
42: import lombok.extern.slf4j.Slf4j;
43:
44: /***********************************************************************************************************************
45: *
46: * A facility to register and unregister global and local DCI contexts.
47: *
48: * @author Fabrizio Giudici
49: *
50: **********************************************************************************************************************/
51: public interface ContextManager
52: {
53: /*******************************************************************************************************************
54: *
55: * A locator for the {@link ContextManager} which uses the {@link ServiceLoader} facility to be independent of
56: * any DI framework.
57: *
58: * This locator caches the internal reference and this is ok for production use; during tests, since multiple
59: * contexts are typically created and destroyed for each test, you should call {@link #reset()} after each test
60: * has been completed.
61: *
62: ******************************************************************************************************************/
63: @Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE)
64: public static final class Locator
65: {
66: private static final LazySupplier<ContextManager> CONTEXT_MANAGER_REF =
67: LazySupplier.of(Locator::findContextManager);
68:
69: private static final LazySupplier<ContextManagerProvider> CONTEXT_MANAGER_PROVIDER_REF =
70: LazySupplier.of(Locator::findContextManagerProvider);
71:
72: /***************************************************************************************************************
73: *
74: **************************************************************************************************************/
75: @Nonnull
76: public static ContextManager find()
77: {
78: return CONTEXT_MANAGER_REF.get();
79: }
80:
81: /***************************************************************************************************************
82: *
83: * <b>This method is for testing only.</b> Sets the global {@link ContextManagerProvider}. See note about
84: * {@link #reset()}.
85: *
86: * @param provider the provider
87: * @see #reset()
88: *
89: **************************************************************************************************************/
90: public static void set (@Nonnull final ContextManagerProvider provider)
91: {
92: CONTEXT_MANAGER_REF.clear();
93: CONTEXT_MANAGER_PROVIDER_REF.set(provider);
94: }
95:
96: /***************************************************************************************************************
97: *
98: * <b>This method is for testing only.</b> Resets the global {@link ContextManagerProvider}; it must be called
99: * at the test completion whenever {@link #set(ContextManagerProvider)} has been called, to avoid polluting the
100: * context of further tests.
101: *
102: * @see #set(ContextManagerProvider)
103: *
104: **************************************************************************************************************/
105: public static void reset()
106: {
107: CONTEXT_MANAGER_REF.clear();
108: CONTEXT_MANAGER_PROVIDER_REF.clear();
109: }
110:
111: /***************************************************************************************************************
112: *
113: **************************************************************************************************************/
114: @Nonnull
115: private static ContextManager findContextManager()
116: {
117: return Objects.requireNonNull(CONTEXT_MANAGER_PROVIDER_REF.get().getContextManager(),
118: "Cannot find ContextManager");
119: }
120:
121: /***************************************************************************************************************
122: *
123: **************************************************************************************************************/
124: @Nonnull
125: private static ContextManagerProvider findContextManagerProvider()
126: {
127: final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
128: final Iterator<ContextManagerProvider> i =
129: ServiceLoader.load(ContextManagerProvider.class, classLoader).iterator();
130:
131: if (!i.hasNext())
132: {
133: throw new RuntimeException("No ServiceProvider for ContextManagerProvider");
134: }
135:
136: final ContextManagerProvider contextManagerProvider = Objects.requireNonNull(i.next(),
137: "contextManagerProvider is null");
138: assert contextManagerProvider != null; // for SpotBugs
139: log.info("ContextManagerProvider instantiated from META-INF: {}", contextManagerProvider);
140: return contextManagerProvider;
141: }
142: }
143:
144: @FunctionalInterface
145: public static interface RunnableWithException<E extends Throwable>
146: {
147: public void run()
148: throws E;
149: }
150:
151: @FunctionalInterface
152: public static interface SupplierWithException<T, E extends Throwable>
153: {
154: public T get()
155: throws E;
156: }
157:
158: /*******************************************************************************************************************
159: *
160: * Returns the list of current contexts, ordered by their priority.
161: *
162: * @return the list of current contexts
163: *
164: ******************************************************************************************************************/
165: @Nonnull
166: public List<Object> getContexts();
167:
168: /*******************************************************************************************************************
169: *
170: * Finds a current context instance of the given type.
171: *
172: * @param <T> the static context type
173: * @param contextType the dynamic context type
174: * @return the requested context
175: * @throws NotFoundException if no context of that type is found
176: *
177: ******************************************************************************************************************/
178: @Nonnull
179: public <T> T findContextOfType (@Nonnull Class<T> contextType)
180: throws NotFoundException;
181:
182: /*******************************************************************************************************************
183: *
184: * Adds a global context.
185: *
186: * @param context the new context
187: *
188: ******************************************************************************************************************/
189: public void addGlobalContext (@Nonnull Object context);
190:
191: /*******************************************************************************************************************
192: *
193: * Removes a global context.
194: *
195: * @param context the context
196: *
197: ******************************************************************************************************************/
198: public void removeGlobalContext (@Nonnull Object context);
199:
200: /*******************************************************************************************************************
201: *
202: * Adds a local context.
203: *
204: * @param context the new context
205: *
206: ******************************************************************************************************************/
207: public void addLocalContext (@Nonnull Object context);
208:
209: /*******************************************************************************************************************
210: *
211: * Removes a local context.
212: *
213: * @param context the context
214: *
215: ******************************************************************************************************************/
216: public void removeLocalContext (@Nonnull Object context);
217:
218: /*******************************************************************************************************************
219: *
220: * Runs a {@link Task} associated with a new local context.
221: *
222: * @param <V> the type of the returned value
223: * @param <T> the type of the exception that can be thrown
224: * @param context the context
225: * @param task the task
226: * @return the value produced by the task
227: * @throws T the exception(s) thrown by the task
228: * @deprecated Use {@link #runWithContexts(Runnable, Object...)} or {@link #runWithContexts(Supplier, Object...)}
229: *
230: ******************************************************************************************************************/
231: @Deprecated
232: public default <V, T extends Throwable> V runWithContext (@Nonnull final Object context,
233: @Nonnull final Task<V, T> task)
234: throws T
235: {
236: return runWithContexts(Collections.singletonList(context), task);
237: }
238:
239: /*******************************************************************************************************************
240: *
241: * Runs a {@link Task} associated with a new bunch of local contexts.
242: *
243: * @param <V> the type of the returned value
244: * @param <T> the type of the exception that can be thrown
245: * @param contexts the contexts
246: * @param task the task
247: * @return the value produced by the task
248: * @throws T the exception(s) thrown by the task
249: * @deprecated Use {@link #runWithContexts(Runnable, Object...)} or {@link #runWithContexts(Supplier, Object...)}
250: *
251: ******************************************************************************************************************/
252: @Deprecated
253: public default <V, T extends Throwable> V runWithContexts (@Nonnull final List<Object> contexts,
254: @Nonnull final Task<V, T> task)
255: throws T
256: {
257: return runEWithContexts(task::run, contexts.toArray());
258: }
259:
260: /*******************************************************************************************************************
261: *
262: * Runs a task associated with a new local context. This variant fits functional interfaces.
263: *
264: * @param <V> the type of the returned value of the task
265: * @param context the context
266: * @param task the task
267: * @return the value produced by the task
268: * @deprecated Use {@link #runWithContexts(Runnable, Object...)} or {@link #runWithContexts(Supplier, Object...)}
269: *
270: ******************************************************************************************************************/
271: @Deprecated
272: public default <V> V runWithContext (@Nonnull final Object context, @Nonnull final Supplier<V> task)
273: {
274: return runWithContexts(task, context);
275: }
276:
277: /*******************************************************************************************************************
278: *
279: * Runs a task associated with a new bunch of local contexts. This variant fits functional interfaces.
280: *
281: * @param <V> the type of the returned value
282: * @param contexts the contexts
283: * @param task the task
284: * @return the value produced by the task
285: * @deprecated Use {@link #runWithContexts(Runnable, Object...)} or {@link #runWithContexts(Supplier, Object...)}
286: *
287: ******************************************************************************************************************/
288: @Deprecated
289: public default <V> V runWithContexts (@Nonnull final List<Object> contexts, @Nonnull final Supplier<V> task)
290: {
291: return runWithContexts(task, contexts.toArray());
292: }
293:
294: /*******************************************************************************************************************
295: *
296: * Calls a runnable with some local contexts. This method fits functional interfaces.
297: *
298: * @param runnable the runnable
299: * @param contexts the contexts
300: * @since 3.2-ALPHA-12
301: *
302: ******************************************************************************************************************/
303: public default void runWithContexts (@Nonnull final Runnable runnable, @Nonnull final Object ... contexts)
304: {
305: final SupplierWithException<Void, RuntimeException> se = () ->{ runnable.run(); return null; };
306: runEWithContexts(se, contexts);
307: }
308:
309: /*******************************************************************************************************************
310: *
311: * Calls a supplier with some local contexts. This method fits functional interfaces.
312: *
313: * @param <T> the type of the result
314: * @param supplier the supplier
315: * @param contexts the contexts
316: * @return the value returned by the supplier
317: * @since 3.2-ALPHA-12
318: *
319: ******************************************************************************************************************/
320: @Nonnull
321: public default <T> T runWithContexts (@Nonnull final Supplier<T> supplier, @Nonnull final Object ... contexts)
322: {
323: final SupplierWithException<T, RuntimeException> se = supplier::get;
324: return runEWithContexts(se, contexts);
325: }
326:
327: /*******************************************************************************************************************
328: *
329: * Calls a runnable with some local contexts. This method fits functional interfaces.
330: *
331: * @param <E> the type of the thrown exception
332: * @param runnable the runnable to call
333: * @param contexts the contexts
334: * @throws E the original exception thrown by task
335: * @since 3.2-ALPHA-12
336: *
337: ******************************************************************************************************************/
338: public default <E extends Throwable> void runEWithContexts (@Nonnull final RunnableWithException<E> runnable,
339: @Nonnull final Object ... contexts)
340: throws E
341: {
342: final SupplierWithException<Void, E> se = () ->{ runnable.run(); return null; };
343: runEWithContexts(se, contexts);
344: }
345:
346: /*******************************************************************************************************************
347: *
348: * Calls a task with some local contexts. This method fits functional interfaces.
349: *
350: * @param <T> the type of the returned value
351: * @param <E> the type of the thrown exception
352: * @param task the task to call
353: * @param contexts the contexts
354: * @return the value returned by the supplier
355: * @throws E the original exception thrown by task
356: * @since 3.2-ALPHA-12
357: *
358: ******************************************************************************************************************/
359: @Nonnull
360: public <T, E extends Throwable> T runEWithContexts (@Nonnull SupplierWithException<T, E> task,
361: @Nonnull Object ... contexts)
362: throws E;
363:
364: /*******************************************************************************************************************
365: *
366: * Creates a binder that makes it possible to bind a local context by means of a try-with-resources instead of a
367: * try/finally.
368: *
369: * <pre>
370: * try (final ContextManager.Binder binder = contextManager.binder(context))
371: * {
372: * ...
373: * }
374: * </pre>
375: *
376: * @param contexts the contexts
377: * @return a binder that can be used in try-with-resources
378: * @since 3.2-ALPHA-12
379: *
380: ******************************************************************************************************************/
381: @Nonnull
382: public default Binder binder (@Nonnull final Object ... contexts)
383: {
384: return new Binder(this, contexts);
385: }
386:
387: /*******************************************************************************************************************
388: *
389: * Used by
390: * @since 3.2-ALPHA-12
391: *
392: ******************************************************************************************************************/
393: public static class Binder implements AutoCloseable
394: {
395: @Nonnull
396: private final ContextManager contextManager;
397:
398: @Nonnull
399: private final Object[] contexts;
400:
401: private Binder (@Nonnull final ContextManager contextManager, @Nonnull final Object[] contexts)
402: {
403: this.contextManager = contextManager;
404: this.contexts = contexts;
405:
406: for (final Object context : contexts)
407: {
408: this.contextManager.addLocalContext(context);
409: }
410: }
411:
412: @Override
413: public void close()
414: {
415: for (final Object context : contexts)
416: {
417: this.contextManager.removeLocalContext(context);
418: }
419: }
420: }
421: }